Vercel
Guides

Accessing Previews

Learn how to display v0 chat previews from the Platform API v2

Use chats.getPreview to display the live preview for a chat in your own product. The endpoint returns a preview URL plus a short-lived token for accessing that URL.

1. Get the preview URL

Call chats.getPreview with your v0 API key. If the preview is still starting, preview is null; poll until it is present.

import { v0 } from 'v0-sdk'

const { preview } = await v0.chats.getPreview({
  chatId: 'chat_abc123',
})

if (!preview) {
  // Try again shortly.
  return
}

2. Cache preview

The preview URL and token remain valid until expiresAt. Cache them to avoid an API round-trip on every iframe subrequest (assets, navigation, etc.).

import { v0 } from 'v0-sdk'

type Preview = { url: string; token: string; expiresAt: string }

// Simple in-memory cache - use Redis or similar in production
const previewCache = new Map<string, Preview>()

async function getCachedPreview(chatId: string) {
  const cached = previewCache.get(chatId)
  const now = Date.now()

  // Return cached preview if still valid (with 60s buffer)
  if (cached && new Date(cached.expiresAt).getTime() - now > 60_000) {
    return cached
  }

  const { preview } = await v0.chats.getPreview({ chatId })
  if (preview) {
    previewCache.set(chatId, preview)
  }
  return preview
}

3. Proxy browser requests

Browsers cannot attach custom headers to an iframe navigation. Serve the preview through your own backend route, and have that route forward every path under the iframe URL to preview.url with the preview token.

async function proxyPreviewRequest(
  request: Request,
  chatId: string,
  path: string[],
) {
  const preview = await getCachedPreview(chatId)

  if (!preview) {
    return new Response('Preview is not ready', { status: 202 })
  }

  const incomingUrl = new URL(request.url)
  const upstreamUrl = new URL(`/${path.join('/')}`, preview.url)
  upstreamUrl.search = incomingUrl.search

  const headers = new Headers(request.headers)
  headers.set('x-v0-preview-token', preview.token)
  headers.delete('host')

  const hasBody = request.method !== 'GET' && request.method !== 'HEAD'
  const response = await fetch(upstreamUrl, {
    method: request.method,
    headers,
    body: hasBody ? request.body : undefined,
    redirect: 'manual',
  })

  return new Response(response.body, {
    status: response.status,
    headers: response.headers,
  })
}

Point your iframe at your proxy URL, not directly at the returned preview.url. Make sure the proxy route also handles subpaths for assets and in-app requests.

<iframe src="/api/v0-preview/chat_abc123/" />

4. Handle token expiry

If your proxy receives an unauthorized response from the preview URL, clear the cache and retry chats.getPreview.

Do not send your v0 API key to the preview URL or expose it to the browser. The API key is only for calling the Platform API; preview access uses the short-lived x-v0-preview-token header.

On this page